/*
 * SystemFactory.c
 *
 *  Created on: 21.05.2011
 *      Author: sili
 */

#include "SystemFactory.h"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("at.sili.aalsystem.factories.SystemFactory"));

static pthread_rwlock_t systemFactoryRwLock = PTHREAD_RWLOCK_INITIALIZER;

SystemFactory::SystemFactory(Controller * _controller, const char * _configDir)
	: ActionListener("SystemFactory"){

	AAL_DEBUG(logger, "Constructor");

	this->configDir.clear();
	this->configDir.append(_configDir);

	this->controller = _controller;
	this->controller->addListener(this);

	systemXmlFile.clear();
	systemXmlFile.append(_configDir);
	systemXmlFile.append("/system.xml");

	this->visualizer = NULL;

	this->system = new AALSystemItem(cOsd::OsdWidth(), cOsd::OsdHeight(),this->controller);

	// check if an system.xml file exists
	char * targetName = canonicalize_file_name(systemXmlFile.c_str());
	if(!targetName){
		// file doesn't exists
		AAL_INFO(logger, "Systemfile %s file doesn't exists. Creating a new one.", systemXmlFile.c_str());
		serializeSystem();
	}
	delete(targetName);
	deSerializeSystemFile();
}

SystemFactory::~SystemFactory() {
	AAL_DEBUG(logger, "Destructor");
	serializeSystem();
}

const char * SystemFactory::getConfigDir(){
	return this->configDir.c_str();
}

AALItem * SystemFactory::getSystem(){
	return this->system;
}

string * SystemFactory::importFlatSvg(const char * _flatSvgFile){

	AAL_INFO(logger, "Importing flat svg file:%s!!!",_flatSvgFile);

	mutex.Lock();

	const int length = 100;
	char integerBuffer[length];
	string buffer = "";
	string svgCopy(configDir);

	XercesDOMParser parser;
	DOMElement * root;

	DOMDocument * doc = Tools::createDOMFromXmlFile(_flatSvgFile, &parser);
	if(doc == NULL){
		AAL_WARN(logger,"Exception while reading SVG file:%s ", _flatSvgFile);
		const char * msg = "Exception while reading SVG file. Sorry, can't import this file.";
		this->controller->publish(doLog,msg);
		mutex.Unlock();
		return NULL;
	}
	root = doc->getDocumentElement();
	Tools::getStringAttributeValue(root, "aal:type", &buffer);

	if(buffer.size() > 0){
		int id = Tools::getIntAttributeValue(root,"aal:id");
		if(id <= 0){
			// file doesn't exists -> warn and abort
			AAL_WARN(logger, "Can't get the aal:id attribute from root Node. Can't parse flat SVG file !!!");
			const char * msg = "Can't get aal:id attribute from the given svg file. Sorry, can't import this file.";
			this->controller->publish(doLog,msg);
			mutex.Unlock();
			return NULL;
		}

		// create a copy of the _flatSvgFile
		snprintf(integerBuffer, (size_t)length-1, "%d", id);
		svgCopy.append("/");
		svgCopy.append(integerBuffer);
		svgCopy.append(".svg");
		Tools::createXmlFileFromDOMDocument(svgCopy.c_str(),doc);

		// get areas (x,y,width, height) for every element in this svg
		map<string, AALArea> areas;

		char cmd[500];
		snprintf(cmd, (size_t)499, "inkscape --query-all %s", svgCopy.c_str());
		AAL_DEBUG(logger, "Executing cmd: %s", cmd);
		string cmdResult = "";
		Tools::execCmd(cmd, &cmdResult);
		AAL_DEBUG(logger, "Cmd returned: %s", cmdResult.c_str());

		vector<string> rows;
		Tools::split((char*)cmdResult.c_str(), "\n", &rows);
		for (unsigned int rowIdx=0; rowIdx < rows.size(); rowIdx++){
			char * row = (char*)rows[rowIdx].c_str();
			vector<string> cols;
			Tools::split(row, ",", &cols);
			AALArea area;
			area.left =Tools::getDoubleFromString(cols[1].c_str());
			area.top =Tools::getDoubleFromString(cols[2].c_str());
			area.width =Tools::getDoubleFromString(cols[3].c_str());
			area.height =Tools::getDoubleFromString(cols[4].c_str());
			areas.insert(pair<string,AALArea>(cols[0],area));
		}

		AALItem * importAALlItem = this->controller->createAALItem(buffer.c_str(),id);

		importAALlItem->fromDOMElementParameters(root, "aal:");

		Tools::getStringAttributeValue(root, "id", &buffer);
		importAALlItem->updateArea(buffer.c_str(), areas);

		importAALlItem->fromSVG(doc, root, svgCopy.c_str(), areas);

		this->system->addOrReplaceChild(importAALlItem);

		this->serializeSystem();
		this->deSerializeSystemFile();
	} else {
		AAL_WARN(logger, "Can't find aal:type attribute in the given SVG. Can't parse given SVG file !!!");
		const char * msg = "Can't find aal:type attribute in the given svg file. Sorry, can't import this file.";
		this->controller->publish(doLog,msg);
		mutex.Unlock();
		return NULL;
	}
	mutex.Unlock();
	return new string(svgCopy);
}

void SystemFactory::serializeSystem(){
	// mutex.Lock();
	XMLCh tempStr[100];
	XMLString::transcode("LS", tempStr, 99);
	DOMImplementation * impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
	XMLString::transcode("AALItem", tempStr, 99);
	DOMDocument * doc = impl->createDocument(0, tempStr, 0);

	this->system->toDOMElement(doc,NULL);
	Tools::createXmlFileFromDOMDocument(systemXmlFile.c_str(),doc);
	doc->release();
	AAL_DEBUG(logger, "File %s created", systemXmlFile.c_str());
	// mutex.Unlock();
}

void SystemFactory::deSerializeSystemFile(){
	// mutex.Lock();
	AAL_DEBUG(logger, "File %s", systemXmlFile.c_str());
	XercesDOMParser parser;
	DOMDocument * doc = Tools::createDOMFromXmlFile(systemXmlFile.c_str(), &parser);
	if(doc != NULL){
		DOMElement * item = doc->getDocumentElement();
		this->system->fromDOMElement(item);
	} else {
		AAL_WARN(logger, "Can't deSerializeSystemFile %s", systemXmlFile.c_str());
	}
	// mutex.Unlock();
}

bool SystemFactory::updateAALItem(AALItem * _newAALItem, int _oldId, int _parentId, string * _response){

	AALItem * exisitingNewAALItem = this->system->getAALItem(_newAALItem->getId());
	AALItem * exisitingOldAALItem = this->system->getAALItem(_oldId);
	AALItem * parentAALItem = this->system->getAALItem(_parentId);

	char resBuff[500];

	// check if parent exists
	if(parentAALItem == NULL){
		// NO -> this is an error!!!
		sprintf(resBuff,"Can't save AALItem cause parent with id %d not found. Aborting...", _parentId);
		AAL_WARN(logger, resBuff);
		_response->clear();
		_response->append(resBuff);
		delete(_newAALItem);
		return false;
	}

	if(exisitingNewAALItem == NULL){
		// new one doesn't exists.
		// either a brand new AALItem, or a new aalItem will replace an old AALItem

		if(exisitingOldAALItem != NULL){
			// The new aalItem will replace an existing old AALItem

			// the old with id == 0 must not be deleted !!!
			if(exisitingOldAALItem->getId() == 0){
				sprintf(resBuff,"Can't replace the system AALItem id:%d. Aborting...", exisitingOldAALItem->getId());
				AAL_ERROR(logger, resBuff);
				_response->clear();
				_response->append(resBuff);
				delete(_newAALItem);
				return false;
			}

			int oldId = exisitingOldAALItem->getId();
			bool successfully = exisitingOldAALItem->cloneRelatives(_newAALItem);
			if(successfully){
				// exisitingOldAALItem.children are now in _newAALItem
				// delete the exisitingOldAALItem.
				string response;
				bool oldDeleted = deleteAALItem(_oldId,&response);
				if(oldDeleted){
					sprintf(resBuff,"AALItem id:%d successfully removed and replaced by new one with id %d.", oldId, _newAALItem->getId());
					_response->append(resBuff);
					AAL_INFO(logger, resBuff);
					return true;
				} else {
					_response->append(response.c_str());
					AAL_WARN(logger, resBuff);
					return false;
				}
			} else {
				sprintf(resBuff,"Can't remove old AALItem id:%d and replace it with the new one id:%d. Unknown error ...", oldId, _newAALItem->getId());
				_response->append(resBuff);
				AAL_WARN(logger, resBuff);
				delete(_newAALItem);
				return false;
			}
		} else {
			// a brand new AALItem
			bool successfully = parentAALItem->addOrReplaceChild(_newAALItem);
			if(successfully){
				serializeSystem();
				sprintf(resBuff,"New AALItem with id %d successfully created.", _newAALItem->getId());
				_response->append(resBuff);
				AAL_INFO(logger, resBuff);
				return true;
			} else {
				sprintf(resBuff,"Can't create new AALItem id:%d. Aborting...", _newAALItem->getId());
				_response->clear();
				_response->append(resBuff);
				AAL_ERROR(logger, resBuff);
				delete(_newAALItem);
				return false;
			}
		}
	} else {
		// new one exists.
		// This is just an property update.
		// double check. JS should check that if user changed the id
		// then this new id must not have an existing used id.
		// but let's check it again.
		sprintf(resBuff,"Update properties of an existing AALItem with id %d.", exisitingNewAALItem->getId());
		AAL_DEBUG(logger, resBuff);

		// check if old exists. It should exist and it should have the same id as the new one
		if(exisitingOldAALItem == NULL || exisitingOldAALItem->getId() != exisitingNewAALItem->getId()){
			// NO -> this is an error!!!
			sprintf(resBuff,"Can't replace an old AALItem with the new one because the old one not found or it doesn't have the same id as the new one. Aborting...");
			_response->clear();
			_response->append(resBuff);
			AAL_DEBUG(logger, resBuff);
			delete(_newAALItem);
			return false;
		}

		// if we got here than: old.id == new.id
		// just update the properties.

		int oldId = exisitingOldAALItem->getId();
		bool successfully = exisitingNewAALItem->cloneRelatives(_newAALItem);

		if(successfully){
			serializeSystem();
			sprintf(resBuff,"AALItem id:%d successfully removed and replaced by new CLONE", oldId);
			_response->append(resBuff);
			AAL_INFO(logger, resBuff);
			return true;
		} else {
			sprintf(resBuff,"Can't remove old AALItem id:%d and create a CLONE. Unknown error ...", oldId);
			_response->append(resBuff);
			AAL_WARN(logger, resBuff);
			delete(_newAALItem);
			return false;
		}
	}
}

bool SystemFactory::deleteAALItem(int _id, string * _response){

	AALItem * exisitingToDeleteAALItem = this->system->getAALItem(_id);

	char resBuff[500];

	// check if toDelete exists
	if(exisitingToDeleteAALItem == NULL){
		// NO -> this is an error!!!
		sprintf(resBuff,"Can't delete AALItem with id %d cause not found. Aborting...", _id);
		AAL_DEBUG(logger, resBuff);
		_response->clear();
		_response->append(resBuff);
		return false;
	}

	bool successfully = exisitingToDeleteAALItem->getParent()->eraseChild(exisitingToDeleteAALItem);
	if(successfully){
		delete(exisitingToDeleteAALItem);
		serializeSystem();
	}

	return successfully;

}


void SystemFactory::doAction(AALAction * _action){
	if(_action->type == doImport){
		AAL_INFO(logger, "%s doAction id:%ld type: %s message:\"%s\" aalItemId:%d", ((ActionListener*)this)->getName(),_action->id, _action->typeToString(), _action->message.c_str(), _action->aalItem != NULL ? _action->aalItem->getId():-1);
		pthread_rwlock_rdlock(&systemFactoryRwLock);

		this->controller->publish(doLog,"Shutting down OSD cause of importing new svg file....");
		this->controller->publish(doHide,"");

		string * copyName = importFlatSvg(_action->message.c_str());
		if(copyName != NULL){
			AALAction * templateAction = new AALAction(doTemplate, NULL, copyName->c_str(), 0, this->controller);
			templateAction->data = _action->data;
			this->controller->publish(templateAction);
			delete(copyName);
		} else {
			// ERROR WHILE IMPORTING SVG FILE
			char message [200];
			sprintf(message,"Task %s failed",_action->data.c_str());
			this->controller->publish(doLog,message);
			_action->state = failed;
		}

		pthread_rwlock_unlock(&systemFactoryRwLock);
	}
}

void SystemFactory::doMainSavedAction(){

}
